package readxml;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NameList;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;



public class DomElement {
	
	public static void addText(Element element, String text) {
	    element.appendChild(element.getOwnerDocument().createTextNode(text));
	}
	
	public static Element createElement(Element parent, String name) {
	    Document document;
	    Element element;

	    document = parent.getOwnerDocument();
	    element  = document.createElement(name);

	    parent.appendChild(element);
	    return element;
	}
	
	public static void addEntity(Element element, String entity) {
	    element.appendChild(element.getOwnerDocument().createEntityReference(entity));
	  }
	
	public static void addChildElement(Element element, String name, Object textValue) {
	    Document document = element.getOwnerDocument();
	    Element child = document.createElement(name);
	    element.appendChild(child);
	    if (textValue != null) {
	      String text = textValue.toString();
	      child.appendChild(document.createTextNode(text));
	    }
	  }
	
	public static void cleanText(Node node) {
	    try {
	      NameList childNodes = (NameList) node.getChildNodes();
	      int noChildren = childNodes.getLength();
	      Node n = null;
	      short type = 0;
	      Vector<Node> rem = new Vector<Node>();
	      for (int i = 0; i < noChildren; i++) {
	        n = ((NodeList) childNodes).item(i);
	        type = n.getNodeType();
	        if (type == Node.TEXT_NODE) {
	          rem.add(n);
	        } else if (type == Node.ELEMENT_NODE) {
	          cleanText(n);
	        }
	      }
	      for (int i = 0; i < rem.size(); i++) {
	        node.removeChild((Node) rem.get(i));
	      }
	    } catch (Exception e) {
	      //DebugUtil.debug(e);
	    }
	  }
	
	private static void trimEmptyTextNodes(Node node) {
	    Element element = null;
	    if (node instanceof Document) {
	      element = ((Document) node).getDocumentElement();
	    } else if (node instanceof Element) {
	      element = (Element) node;
	    } else {
	      return;
	    }

	    List<Node> nodesToRemove = new ArrayList<Node>();
	    NodeList children = element.getChildNodes();
	    for (int i = 0; i < children.getLength(); i++) {
	      Node child = children.item(i);
	      if (child instanceof Element) {
	        trimEmptyTextNodes(child);
	      } else if (child instanceof Text) {
	        Text t = (Text) child;
	        if (t.getData().trim().length() == 0) {
	          nodesToRemove.add(child);
	        }
	      }
	    }

	    for (Node n : nodesToRemove) {
	      element.removeChild(n);
	    }
	  }

	  public static void compareNodes(Node expected, Node actual, boolean trimEmptyTextNodes)
	      throws Exception {
	    if (trimEmptyTextNodes) {
	      trimEmptyTextNodes(expected);
	      trimEmptyTextNodes(actual);
	    }
	    compareNodes(expected, actual);
	  }

	  public static void compareNodes(Node expected, Node actual) throws Exception {
	    if (expected.getNodeType() != actual.getNodeType()) {
	      throw new Exception("Different types of nodes: " + expected + " " + actual);
	    }
	    if (expected instanceof Document) {
	      Document expectedDoc = (Document) expected;
	      Document actualDoc = (Document) actual;
	      compareNodes(expectedDoc.getDocumentElement(), actualDoc.getDocumentElement());
	    } else if (expected instanceof Element) {
	      Element expectedElement = (Element) expected;
	      Element actualElement = (Element) actual;

	      // compare element names
	      if (!expectedElement.getLocalName().equals(actualElement.getLocalName())) {
	        throw new Exception("Element names do not match: " + expectedElement.getLocalName() + " "
	            + actualElement.getLocalName());
	      }
	      // compare element ns
	      String expectedNS = expectedElement.getNamespaceURI();
	      String actualNS = actualElement.getNamespaceURI();
	      if ((expectedNS == null && actualNS != null)
	          || (expectedNS != null && !expectedNS.equals(actualNS))) {
	        throw new Exception("Element namespaces names do not match: " + expectedNS + " " + actualNS);
	      }

	      String elementName = "{" + expectedElement.getNamespaceURI() + "}"
	          + actualElement.getLocalName();

	      // compare attributes
	      NamedNodeMap expectedAttrs = expectedElement.getAttributes();
	      NamedNodeMap actualAttrs = actualElement.getAttributes();
	      if (countNonNamespaceAttribures(expectedAttrs) != countNonNamespaceAttribures(actualAttrs)) {
	        throw new Exception(elementName + ": Number of attributes do not match up: "
	            + countNonNamespaceAttribures(expectedAttrs) + " "
	            + countNonNamespaceAttribures(actualAttrs));
	      }
	      for (int i = 0; i < expectedAttrs.getLength(); i++) {
	        Attr expectedAttr = (Attr) expectedAttrs.item(i);
	        if (expectedAttr.getName().startsWith("xmlns")) {
	          continue;
	        }
	        Attr actualAttr = null;
	        if (expectedAttr.getNamespaceURI() == null) {
	          actualAttr = (Attr) actualAttrs.getNamedItem(expectedAttr.getName());
	        } else {
	          actualAttr = (Attr) actualAttrs.getNamedItemNS(expectedAttr.getNamespaceURI(),
	              expectedAttr.getLocalName());
	        }
	        if (actualAttr == null) {
	          throw new Exception(elementName + ": No attribute found:" + expectedAttr);
	        }
	        if (!expectedAttr.getValue().equals(actualAttr.getValue())) {
	          throw new Exception(elementName + ": Attribute values do not match: "
	              + expectedAttr.getValue() + " " + actualAttr.getValue());
	        }
	      }

	      // compare children
	      NodeList expectedChildren = expectedElement.getChildNodes();
	      NodeList actualChildren = actualElement.getChildNodes();
	      if (expectedChildren.getLength() != actualChildren.getLength()) {
	        throw new Exception(elementName + ": Number of children do not match up: "
	            + expectedChildren.getLength() + " " + actualChildren.getLength());
	      }
	      for (int i = 0; i < expectedChildren.getLength(); i++) {
	        Node expectedChild = expectedChildren.item(i);
	        Node actualChild = actualChildren.item(i);
	        compareNodes(expectedChild, actualChild);
	      }
	    } else if (expected instanceof Text) {
	      String expectedData = ((Text) expected).getData().trim();
	      String actualData = ((Text) actual).getData().trim();

	      if (!expectedData.equals(actualData)) {
	        throw new Exception("Text does not match: " + expectedData + " " + actualData);
	      }
	    }
	  }

	  private static int countNonNamespaceAttribures(NamedNodeMap attrs) {
	    int n = 0;
	    for (int i = 0; i < attrs.getLength(); i++) {
	      Attr attr = (Attr) attrs.item(i);
	      if (!attr.getName().startsWith("xmlns")) {
	        n++;
	      }
	    }
	    return n;
	  }
	  
	  public static Element createNewElementAndSet(Document document,
		      Element parent, String childElement, String childValue) {
		    Element child = (Element) document.createElement(childElement);
		    parent.appendChild(child);
		    child.setNodeValue(childValue);
		    child.appendChild(document.createTextNode(childValue));
		    return child;
		  }
	  
	  public static Element createNewElementAndSetAndAttribute(Document document,
		      Element parent, String childElement, String childValue,
		      String attributeName, String attributeValue) {
		    Element child = createNewElementAndSet(document, parent, childElement,
		        childValue);
		    child.setAttribute(attributeName, attributeValue);
		    return child;
		  }
	  
	  public static Element createElement(Document document, String name) {
		    return document.createElement(name);
		  }
	  
	  
	  public static Element getFirstElement(Node parent) {
		    Node n = parent.getFirstChild();
		    while (n != null && Node.ELEMENT_NODE != n.getNodeType()) {
		        n = n.getNextSibling();
		    }
		    if (n == null) {
		        return null;
		    }
		    return (Element)n;
		}
	  
		  public static Element getNextElement(Element el) {
		    Node nd = el.getNextSibling();
		    while (nd != null) {
		        if (nd.getNodeType() == Node.ELEMENT_NODE) {
		            return (Element)nd;
		        }
		        nd = nd.getNextSibling();
		    }
		    return null;
		}

		public void deleteFirstElement(Document doc) {
		        Element root = doc.getDocumentElement();
		        Element child = (Element)root.getFirstChild();
		        root.removeChild(child);
		    }
}

